package com.tianji.remark.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author Ys
 * @since 2023-08-13
 */
@Service
@Transactional
public class LikedRecordRedisServiceImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Autowired
    private RabbitTemplate rabbitTemplate;

    //1. 基于前端的参数，判断是执行点赞还是取消点赞
    //2.判断是否执行成功，如果失败，则直接结束
    //3.如果执行成功，统计点赞总数
    //4.发送MQ通知

    // 点赞或取消点赞
    @Override
    public void addLikeRecord(LikeRecordFormDTO recordDTO) {
      //1. 基于前端的参数，判断是执行点赞还是取消点赞
        Boolean flag = recordDTO.getLiked() ? liked(recordDTO) : unliked(recordDTO);
        //2. 判断是否执行成功，如果失败，则直接结束
        if(!flag){
            return;//程序结束
        }
        //3. 如果执行成功，统计点赞总数
        String likedKey="likes:biz"+recordDTO.getBizId();
        Long number = stringRedisTemplate.opsForSet().size(likedKey);
        //4. 点赞数量存入redis
        String likedNumKey = "likes:times:type:" + recordDTO.getBizType();
        stringRedisTemplate.opsForZSet().add(likedNumKey,recordDTO.getBizId().toString(),number);
    }

    // 抽取处理点赞方法
    private Boolean liked(LikeRecordFormDTO recordDTO) {
        //1. 直接向Redis中保存点赞数据
        String likedKey = "likes:biz:" + recordDTO.getBizId();
        Long num = stringRedisTemplate.opsForSet().add(likedKey, UserContext.getUser().toString());
        //2. 返回结果 line: 1 表示成功 0 表示失败
        return num != null && num>0;
    }

    // 抽取处理取消点赞方法
    private Boolean unliked(LikeRecordFormDTO recordDTO) {
      //1. 直接向Redis中删除点赞数据
        String likedKey="likes:biz:"+recordDTO.getBizId();
        Long num = stringRedisTemplate.opsForSet().remove(likedKey, UserContext.getUser().toString());
        //2. 返回结果 line: 1表示成功 0 表示失败
        return num != null && num >0;
    }

    // 批量查询我的点赞状态
    @Override
    public Set<Long> getLikedIds(List<Long> bizIds) {
        //查询那些是我的点赞业务id

        List<Object> resultList = stringRedisTemplate.executePipelined(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                StringRedisConnection conn = (StringRedisConnection) connection;
                for (Long bizId : bizIds) {
                    conn.sIsMember("likes:biz:" + bizId, UserContext.getUser().toString());
                }
                return null;
            }
        });

        //lambda写法收集
        return IntStream.range(0,resultList.size())
                .filter(index -> (boolean) resultList.get(index))
                .mapToObj(index -> bizIds.get(index))
                .collect(Collectors.toSet());
    }



    // 要遍历的业务类型
    private static final List<String> BIZ_TYPES = List.of("QA", "NOTE");

    // 每次获取的最大数量
    private static final int MAX_SIZE = 30;

    // 从redis查询点赞数量，发送MQ消息
    @Override
    public void readLikedTimesAndSendMQ() {
      //获取所有记录点赞数量的Key
        for (String bizType : BIZ_TYPES) {
            //读取并移除指定key
            String key="likes:times:type:"+bizType;
            Set<ZSetOperations.TypedTuple<String>> tuples = stringRedisTemplate.opsForZSet().popMin(key, MAX_SIZE);
            if(CollectionUtil.isEmpty(tuples)){
                continue;
            }
            //2.数据转换
            ArrayList<LikedTimesDTO> likedTimesDTOList = new ArrayList<>();
            for (ZSetOperations.TypedTuple<String> tuple : tuples) {
                if(tuple.getScore() == null || tuple.getValue()==null){
                    continue;
                }
                likedTimesDTOList.add((LikedTimesDTO) LikedTimesDTO.of(Long.valueOf(tuple.getValue()), tuple.getScore().intValue()));
                //3.发送MQ消息
                rabbitTemplate.convertAndSend(MqConstants.Exchange.LIKE_RECORD_EXCHANGE,
                        StrUtil.format(MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE,bizType),
                        likedTimesDTOList
                        );
            }
        }
    }
}
